﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.LiveFX.Client;
using Microsoft.LiveFX.ResourceModel;

namespace DeSleeper.Library
{
    public class MeshData
    {
        private const string DESLEEPER_DATA = "DeSleeperData";
        private const string WAKEUP_REQUESTS = "WakeUpRequests";

        public CloudSettings Settings { get; private set; }
        public LiveOperatingEnvironment LiveEnvironment { get; private set;}
        public event CancelDataEventHandler<WakeUpRequest> ProcessingRequest;
        public event DataEventHandler<WakeUpRequest> ProcessRequest;
        private static Dictionary<CloudSettings, MeshData> _dataSets = new Dictionary<CloudSettings, MeshData>();

        protected MeshData(CloudSettings settings)
        {
            Settings = settings;
            LiveEnvironment = new LiveOperatingEnvironment();
        }
        
        public static MeshData GetFromCloud(CloudSettings settings)
        {
            MeshData data;
            var accessOptions = new LiveItemAccessOptions(true, true);
            if (!_dataSets.TryGetValue(settings, out data))
            {
                data = new MeshData(settings);
                _dataSets.Add(settings, data);
                var token =
                    new NetworkCredential(settings.UserID, settings.Password, settings.Uri).
                        GetWindowsLiveAuthenticationToken();

                data.LiveEnvironment.Connect(token, AuthenticationTokenType.UserToken, new Uri(settings.Uri), accessOptions);
            }
            return data;
        }

        private MeshObject _data;
        
        /// <summary>
        /// The data object that holds all deSleeper related data in Live Mesh.
        /// </summary>
        protected MeshObject Data
        {
            get
            {
                if (_data == null)
                {
                    _data =
                        (from meshObject in LiveEnvironment.Mesh.CreateQuery<MeshObject>()
                         where string.Equals(meshObject.Resource.Type, DESLEEPER_DATA,
                                           StringComparison.InvariantCulture)
                         select meshObject).FirstOrDefault();
                    
                    if (_data == null)
                    {
                        _data = new MeshObject(DESLEEPER_DATA);
                        LiveEnvironment.Mesh.MeshObjects.Add(ref _data);
                    }
                }
                return _data;
            }
        }

        public void RegisterWithLiveMesh()
        {
            var wakeUpRequests = getWakeUpRequests();
            processRequests(wakeUpRequests);
            wakeUpRequests.DataEntries.ChangeNotificationReceived += wakeUpRequestsChanged;
        }

        /// <summary>
        /// Adds a request to wake up a machine to the Live Mesh queue.
        /// </summary>
        /// <param name="hostName">Host name or IP address of the target.</param>
        public void AddWakeRequest(string hostName)
        {
            var wakeUpRequests = getWakeUpRequests();

            var request = new WakeUpRequest();
            var requestEntry = new DataEntry(hostName);
            requestEntry.Resource.SetUserData(request);
            wakeUpRequests.DataEntries.Add(ref requestEntry);
        }

        private void wakeUpRequestsChanged(object sender, EventArgs e)
        {
            var wakeUpRequests = getWakeUpRequests();
            processRequests(wakeUpRequests);
        }

        private void processRequests(DataFeed requests)
        {
            foreach (var entry in requests.DataEntries.Entries)
            {
                var request = entry.Resource.GetUserData<WakeUpRequest>();
                if (ProcessingRequest != null)
                {
                    var args = CancelDataEventArgs.Create(request, true);
                    ProcessingRequest(this, args);
                    if (args.Cancel) continue;
                }
                requests.DataEntries.Remove(entry);
                if (ProcessRequest != null)
                    ProcessRequest(this, DataEventArgs.Create(request));
            }
        }

        private DataFeed getWakeUpRequests()
        {
            var wakeUpRequests =
                (from feed in Data.CreateQuery<DataFeed>()
                 where string.Equals(feed.Resource.Type, WAKEUP_REQUESTS,
                                     StringComparison.InvariantCulture)
                 select feed).FirstOrDefault();

            if (wakeUpRequests == null)
            {
                wakeUpRequests = new DataFeed(WAKEUP_REQUESTS);
                Data.DataFeeds.Add(ref wakeUpRequests);
            }
            return wakeUpRequests;
        }
    }
}
